#include <stdio.h>
#include <string>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <assert.h>


int base64_encode(const std::string& str, std::string& output, bool with_new_line = false) {
    BIO *b64 = BIO_new(BIO_f_base64());
    BIO *bmem = BIO_new(BIO_s_mem());
    bmem = BIO_push(b64, bmem);
    BUF_MEM *bptr = nullptr;

    if (!with_new_line)
        BIO_set_flags(bmem, BIO_FLAGS_BASE64_NO_NL);

    int ret = BIO_write(bmem, str.data(), str.length());
    if (ret <= 0) return -1;

    ret = BIO_flush(bmem);
    if (ret != 1) return -1;

    BIO_get_mem_ptr(bmem, &bptr);

    output = std::string(bptr->data, bptr->length);

    BIO_free_all(bmem);
    return 0;
}

std::string base64_decode(const std::string& str, bool with_new_line = false) {
    static char buf[512];

    bool allocated = false;
    char *buffer = buf;
    if (str.length() > sizeof(buf)) {
        buffer = (char *) malloc(str.length());
        allocated = true;
    }

    BIO *b64  = BIO_new(BIO_f_base64());
    BIO *bmem = BIO_new_mem_buf(str.data(), str.length());
    bmem = BIO_push(b64, bmem);

    if (!with_new_line)
        BIO_set_flags(bmem, BIO_FLAGS_BASE64_NO_NL);

    int out_len = BIO_read(bmem, buffer, str.length());

    std::string output(buffer, out_len);
    BIO_free_all(bmem);

    if (allocated)
        free(buffer);

    return output;
}

int main(int argc, const char **argv) {
    std::string org("{\"error_code\":-10001,\"error_info\":\"illegal identity\"}");
    std::string after("eyJlcnJvcl9jb2RlIjotMTAwMDEsImVycm9yX2luZm8iOiJpbGxlZ2FsIGlkZW50aXR5In0=");

    std::string after_now;
    int ret = base64_encode(org, after_now);
    assert(ret == 0);

    if (after == after_now)
        printf("base64 encode ok\n");
    else
        printf("encode not ok, \n%s\n%s\n", after.c_str(), after_now.c_str());

    if (org == base64_decode(after))
        printf("base64 decode ok\n");
    else
        printf("decode not ok, %s:%s\n", org.c_str(), base64_decode(after).c_str());

    return 0;
}
